#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hash_map.h"
#include "map_utils.h"
#include "net_defs.h"

int bit_set_num(int bitmap)
{
    int bits = sizeof(int) * 8;
    int cnt = 0;
    for (int i = 0; i < bits; i++) {
        int flag = bitmap & (0x01 << i);
        if (flag) {
            cnt++;
        }
    }
    return cnt;
}

int judge_pkt(pkt_metadata_t* pkt_md)
{
    if (!pkt_md) {
        printf("%s: pkt_md is null, accept by default\n", __FUNCTION__);
        return -1;
    }

    hash_map map_src, map_dst, map_sport, map_dport, map_action, map_proto;
    map_init(&map_src, str_hash, str_equal_f, MAP_CAPACTIY);
    map_init(&map_dst, str_hash, str_equal_f, MAP_CAPACTIY);
    map_init(&map_sport, int_hash, int_equal_f, MAP_CAPACTIY);
    map_init(&map_dport, int_hash, int_equal_f, MAP_CAPACTIY);
    map_init(&map_action, int_hash, int_equal_f, MAP_CAPACTIY);
    map_init(&map_proto, int_hash, int_equal_f, MAP_CAPACTIY);

    load_str_map(&map_src, SRC_MAP_PATH);
    load_str_map(&map_dst, DST_MAP_PATH);
    load_int_map(&map_sport, SPORT_MAP_PATH);
    load_int_map(&map_dport, DPORT_MAP_PATH);
    load_int_map(&map_proto, PROTO_MAP_PATH);
    load_int_map(&map_action, ACTION_MAP_PATH);

    // print_str_map(&map_src, "src map");
    // print_str_map(&map_dst, "dst map");
    // print_int_map(&map_proto, "proto map");

    map_entry* e = map_get(&map_src, pkt_md->src);
    if (e) {
        char* key = (char*)(e->key);
        uint32_t val = *(uint32_t*)(e->val);
        printf("src exist: %s -> val: %d\n", key, val);
        if (pkt_md->bitmap) {
            pkt_md->bitmap &= val;
        } else {
            pkt_md->bitmap = val;
        }
        e = NULL;
    } else {
        e = map_get(&map_src, "others");
        if (e) {
            char* key = (char*)(e->key);
            uint32_t val = *(uint32_t*)(e->val);
            printf("others src: %s -> val: %d\n", key, val);
            if (pkt_md->bitmap) {
                pkt_md->bitmap &= val;
            } else {
                pkt_md->bitmap = val;
            }
            e = NULL;
        }
    }

    e = map_get(&map_dst, pkt_md->dst);
    if (e) {
        char* key = (char*)(e->key);
        uint32_t val = *(uint32_t*)(e->val);
        printf("dst exist: %s -> val: %d\n", key, val);
        if (pkt_md->bitmap) {
            pkt_md->bitmap &= val;
        } else {
            pkt_md->bitmap = val;
        }
        e = NULL;
    } else {
        e = map_get(&map_dst, "others");
        if (e) {
            char* key = (char*)(e->key);
            uint32_t val = *(uint32_t*)(e->val);
            printf("others src: %s -> val: %d\n", key, val);
            if (pkt_md->bitmap) {
                pkt_md->bitmap &= val;
            } else {
                pkt_md->bitmap = val;
            }
            e = NULL;
        }
    }

    e = map_get(&map_sport, &(pkt_md->sport[0]));
    if (e) {
        uint32_t key = *(uint32_t*)(e->key);
        uint32_t val = *(uint32_t*)(e->val);
        printf("sport exist: %d -> val: %d\n", key, val);
        if (pkt_md->bitmap) {
            pkt_md->bitmap &= val;
        } else {
            pkt_md->bitmap = val;
        }
        e = NULL;
    }

    e = map_get(&map_dport, &(pkt_md->dport[0]));
    if (e) {
        uint32_t key = *(uint32_t*)(e->key);
        uint32_t val = *(uint32_t*)(e->val);
        printf("dport exist: %d -> val: %d\n", key, val);
        if (pkt_md->bitmap) {
            pkt_md->bitmap &= val;
        } else {
            pkt_md->bitmap = val;
        }
        e = NULL;
    }

    e = map_get(&map_proto, &(pkt_md->proto));
    if (e) {
        uint32_t key = *(uint32_t*)(e->key);
        uint32_t val = *(uint32_t*)(e->val);
        printf("proto exist: %d -> val: %d\n", key, val);
        if (pkt_md->bitmap) {
            pkt_md->bitmap &= val;
        } else {
            pkt_md->bitmap = val;
        }
        e = NULL;
    }

    // 内核 O(1)调度算法: https://blog.csdn.net/Rong_Toa/article/details/108993870
    // https://zhuanlan.zhihu.com/p/464176766
    printf("bitmap: %d\n", pkt_md->bitmap);
    if (pkt_md->bitmap) {
        if (bit_set_num(pkt_md->bitmap) > 1) {
            printf("bitmap: bit set num > 1\n");
            pkt_md->bitmap &= -(pkt_md->bitmap);
        }
    }

    e = map_get(&map_action, &(pkt_md->bitmap));
    if (e) {
        uint32_t key = *(uint32_t*)(e->key);
        uint32_t val = *(uint32_t*)(e->val);
        printf("action exist: %d -> val: %d\n", key, val);
        pkt_md->action = val;
        e = NULL;
    }

    return 0;
}

int main(int argc, char** argv)
{
    int opt;
    int option_index = 0;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s [s:d:m:n:p:h:]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    struct option long_options[] = {
        { "src", required_argument, NULL, 's' },
        { "dst", required_argument, NULL, 'd' },
        { "sport", required_argument, NULL, 'm' },
        { "dport", required_argument, NULL, 'n' },
        { "proto", required_argument, NULL, 'p' },
        { "help", no_argument, NULL, 'h' },
        { 0, 0, 0, 0 },
    };

    pkt_metadata_t pkt_md;
    memset(&pkt_md, 0, sizeof(pkt_metadata_t));
    while ((opt = getopt_long(argc, argv, "s:d:m:n:p:j:h", long_options, &option_index)) != -1) {
        switch (opt) {
            case 's':
                pkt_md.src = strdup(optarg);
                break;
            case 'd':
                pkt_md.dst = strdup(optarg);
                break;
            case 'm': {
                if (optarg) {
                    char* tok = strtok(optarg, ",");
                    while (tok) {
                        int i = 0;
                        while (pkt_md.sport[i])
                            i++;
                        pkt_md.sport[i] = atoi(tok);
                        tok = strtok(NULL, ",");
                    }
                }
            } break;
            case 'n': {
                if (optarg) {
                    char* tok = strtok(optarg, ",");
                    while (tok) {
                        int i = 0;
                        while (pkt_md.dport[i])
                            i++;
                        pkt_md.dport[i] = atoi(tok);
                        tok = strtok(NULL, ",");
                    }
                }
            } break;
            case 'p':
                pkt_md.proto = get_proto(optarg);
                break;
            default: /* '?' */
                fprintf(stderr, "Usage: %s [s:d:m:n:p:j:h]\n", argv[0]);
                exit(EXIT_FAILURE);
        }
    }
    print_pkt_md(&pkt_md, "input");
    int result = judge_pkt(&pkt_md);

    print_pkt_md(&pkt_md, "result");

    printf("judge_pkt: %s\n", get_action_str(pkt_md.action));

    return EXIT_SUCCESS;
}